﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SafeScript.MetaModel.Statements
{
    public class DictContentOperator : Operator
    {
        public Dictionary<string, Operator> Values = new Dictionary<string, Operator>();

        private ITypeDef Type;

        public override int GetPrecedence()
        {
            return 0;
        }

        public override ITypeDef ComputeType(ITypeDef goal, bool needsRef, ErrorReport report)
        {
            if (needsRef)
            {
                this.ReportNotAReferenceType(report);
                return null;
            }

            // Type is cached?
            if (this.Type != null)
                return this.Type;
            // Is there a goal type that can help to infere the list type?
            if (goal is InstanceType && (goal as InstanceType).Type is TemplateInstanceTypeDef)
            {
                TemplateInstanceTypeDef t = (goal as InstanceType).Type as TemplateInstanceTypeDef;
                // The goal type is a list?
                if (t.Template is DictType && t.TemplateParameters[0].IsEqualTo( StringType.Self ))
                {
                    // Determine the type for every value in the list
                    foreach( string key in this.Values.Keys )
                    {
                        Operator op = this.Values[key];
                        ITypeDef optype = op.ComputeType(t.TemplateParameters[1], false, report);
                        // Some error?
                        if (optype == null)
                            return null;

                        InstanceType it = this.ToInstance(ref op, optype, report);
                        // Some error?
                        if (it == null)
                            return null;
                        this.Values[key] = op;
                        // Is the list element compatible with the template type?
                        if (!(it.CanBeAssignedTo(t.TemplateParameters[0])))
                        {
                            this.ReportIncompatibleTypes(optype, t.TemplateParameters[1], report);
                            return null;
                        }
                    }
                    this.Type = goal;
                    return this.Type;
                }
            }

            // There is no goal type.
            // If the list is empty, the type is unresolved
            if (this.Values.Count == 0)
            {
                report.Add(new ErrorEntry(this.Node) { Type = ErrorType.UnresolvedType });
                return null;
            }

            // All members of the dict must have the same type to support us in type inference.
            ITypeDef t2 = null;
            foreach (Operator op in this.Values.Values)
            {
                ITypeDef optype = op.ComputeType(null, false, report);
                // Some error?
                if (optype == null)
                    return null;
                if (t2 == null)
                    t2 = optype;
                else if (!t2.IsEqualTo(optype))
                    this.ReportIncompatibleTypes(t2, optype, report);
            }
            this.Type = new InstanceType() { Type = DictType.Instantiate(StringType.Self, t2) };
            return this.Type;
        }

        //private void ReportIncompatibleTypes(ITypeDef type1, ITypeDef type2, ErrorReport report)
        //{
        //    report.Add(new ErrorEntry() { Column = this.Node.StartColumn, Line = this.Node.StartLine, Filename = "?", Type = ErrorType.IncompatibleTypes });
        //}
    }
}
